﻿using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Centido.Core.Test
{
    /// <summary>
    ///   Restoring security testing.
    /// </summary>
    [TestClass]
    public class RestoringSecurityTest
    {
        /// <summary>
        ///   Domain name
        /// </summary>
        public const string DomainName = "FullAdminRights";

        /// <summary>
        ///   Domain
        /// </summary>
        private Domain _domain;


        /// <summary>
        ///   Initializing the test
        /// </summary>
        [TestInitialize]
        public void Initialize()
        {
            _domain = Domain.Build(DomainConfiguration.Load(DomainName));

            _domain.DeleteAll();
        }


        /// <summary>
        ///   Cleaning up after the test
        /// </summary>
        [TestCleanup]
        public void CleanUp()
        {
            _domain.DeleteAll();
        }


        /// <summary>
        ///   Backuping and restoring the current domain.
        /// </summary>
        private void BackupAndRestore()
        {
            // Creating a backup
            new BackupTask(_domain).Run();

            // Deleting all tables
            _domain.DeleteAllTables();

            Assert.AreEqual(_domain.Tables.Count, 0);

            // Restoring
            new RestoreTask(_domain).Run();
        }


        /// <summary>
        ///   Restoring user list.
        /// </summary>
        [TestMethod]
        public void RestoringUserList()
        {
            // Creating two users
            var user1Name = StringProperty.RandomIdentifier();
            var user2Name = StringProperty.RandomIdentifier();
            var user1Password = StringProperty.RandomPassword();
            var user2Password = StringProperty.RandomPassword();

            _domain.AddUser(user1Name, user1Password);
            _domain.AddUser(user2Name, user2Password);

            var userCount = _domain.Users.Count;

            BackupAndRestore();

            Assert.AreEqual(_domain.Users.Count, userCount);

            var restoredUser1 = _domain.GetUser(user1Name);
            var restoredUser2 = _domain.GetUser(user2Name);

            Assert.IsNotNull(restoredUser1);
            Assert.IsNotNull(restoredUser2);

            // Trying to login using the user default password
            var domain1 = _domain.Logon(user1Name.ToLower(), user1Password);
            var domain2 = _domain.Logon(user2Name.ToLower(), user2Password);

            Assert.IsNotNull(domain1);
            Assert.IsNotNull(domain2);
        }


        /// <summary>
        ///   Restoring user properties.
        /// </summary>
        [TestMethod]
        public void RestoringUserProperties()
        {
            // Creating a user
            var userName = StringProperty.RandomIdentifier();
            var user = _domain.AddUser(userName, StringProperty.RandomPassword());

            // Setting user properties
            user.CanLogon = false;
            user.CanChangeSchema = true;
            user.CanChangeSecurity = true;
            user.DisplayName = "The user display name";

            BackupAndRestore();

            var restoredUser = _domain.GetUser(userName);

            Assert.IsNotNull(restoredUser);

            Assert.AreEqual(restoredUser.DisplayName, "The user display name");
            Assert.AreEqual(restoredUser.CanChangeSecurity, true);
            Assert.AreEqual(restoredUser.CanChangeSchema, true);
            Assert.AreEqual(restoredUser.CanLogon, false);
        }


        /// <summary>
        ///   Checking that user password are preserved.
        /// </summary>
        [TestMethod]
        public void CheckingThatUserPasswordArePreserved()
        {
            // Creating a user
            var userName = StringProperty.RandomIdentifier();
            var userPassword = StringProperty.RandomPassword();

            _domain.AddUser(userName, userPassword);

            BackupAndRestore();

            // Logging in with the old user password
            _domain.Logon(userName, userPassword);
        }


        /// <summary>
        ///   Restoring role list.
        /// </summary>
        [TestMethod]
        public void RestoringRoleList()
        {
            // Creating two roles
            var role1Name = StringProperty.RandomIdentifier();
            var role2Name = StringProperty.RandomIdentifier();

            _domain.AddRole(role1Name);
            _domain.AddRole(role2Name);

            Assert.AreEqual(_domain.Roles.Count, 2);

            BackupAndRestore();

            Assert.AreEqual(_domain.Roles.Count, 2);

            var role1 = _domain.GetRole(role1Name);
            var role2 = _domain.GetRole(role2Name);

            Assert.IsNotNull(role1);
            Assert.IsNotNull(role2);
        }


        /// <summary>
        ///   Restoring role properties.
        /// </summary>
        [TestMethod]
        public void RestoringRoleProperties()
        {
            // Creating a role
            var roleName = StringProperty.RandomIdentifier();
            var role = _domain.AddRole(roleName);

            // Setting role properties
            role.DisplayName = "The role display name";

            BackupAndRestore();

            var restoredRole = _domain.GetRole(roleName);

            Assert.IsNotNull(restoredRole);

            Assert.AreEqual(restoredRole.DisplayName, "The role display name");
        }


        /// <summary>
        ///   Restoring role users
        /// </summary>
        [TestMethod]
        public void RestoringRoleUsers()
        {
            // Creating a role
            var roleName = StringProperty.RandomIdentifier();
            var role = _domain.AddRole(roleName);

            // Adding two users to the role
            var user1Name = StringProperty.RandomIdentifier();
            var user2Name = StringProperty.RandomIdentifier();

            var user1 = _domain.AddUser(user1Name, StringProperty.RandomPassword());
            var user2 = _domain.AddUser(user2Name, StringProperty.RandomPassword());

            role.AddUser(user1);
            role.AddUser(user2);

            Assert.AreEqual(role.Users.Count, 2);

            BackupAndRestore();

            var restoredRole = _domain.GetRole(roleName);

            Assert.IsNotNull(restoredRole);

            Assert.AreEqual(restoredRole.Users.Count, 2);

            Assert.AreEqual(restoredRole.Users.Count(u => u.Name == user1Name.ToLower()), 1);
            Assert.AreEqual(restoredRole.Users.Count(u => u.Name == user2Name.ToLower()), 1);
        }


        /// <summary>
        ///   Restoring role hierarchy.
        /// </summary>
        [TestMethod]
        public void RestoringRoleHierarchy()
        {
            // Creating role hierarchy
            var roleName = StringProperty.RandomIdentifier();
            var child1RoleName = StringProperty.RandomIdentifier();
            var child2RoleName = StringProperty.RandomIdentifier();
            var top1RoleName = StringProperty.RandomIdentifier();
            var top2RoleName = StringProperty.RandomIdentifier();

            var role = _domain.AddRole(roleName);
            var child1Role = _domain.AddRole(child1RoleName);
            var child2Role = _domain.AddRole(child2RoleName);
            var top1Role = _domain.AddRole(top1RoleName);
            var top2Role = _domain.AddRole(top2RoleName);

            role.AddRole(child1Role);
            role.AddRole(child2Role);
            top1Role.AddRole(role);
            top2Role.AddRole(role);

            BackupAndRestore();

            var restoredRole = _domain.GetRole(roleName);

            Assert.IsNotNull(restoredRole);

            Assert.AreEqual(restoredRole.ChildRoles.Count, 2);
            Assert.AreEqual(restoredRole.ParentRoles.Count, 2);

            Assert.AreEqual(restoredRole.ChildRoles
                .Count(r => r.Name == child1RoleName.ToLower()), 1);
            Assert.AreEqual(restoredRole.ChildRoles
                .Count(r => r.Name == child2RoleName.ToLower()), 1);
            Assert.AreEqual(restoredRole.ParentRoles
                .Count(r => r.Name == top1RoleName.ToLower()), 1);
            Assert.AreEqual(restoredRole.ParentRoles
                .Count(r => r.Name == top2RoleName.ToLower()), 1);
        }


        /// <summary>
        ///   Restoring unit list.
        /// </summary>
        [TestMethod]
        public void RestoringUnitList()
        {
            // Creating a unit list
            var unit1Name = StringProperty.RandomIdentifier();
            var unit2Name = StringProperty.RandomIdentifier();

            _domain.AddUnit(unit1Name);
            _domain.AddUnit(unit2Name);

            Assert.AreEqual(_domain.Units.Count, 2);

            BackupAndRestore();

            Assert.AreEqual(_domain.Units.Count, 2);

            Assert.AreEqual(_domain.Units.Count(u => u.Name == unit1Name.ToLower()), 1);
            Assert.AreEqual(_domain.Units.Count(u => u.Name == unit2Name.ToLower()), 1);
        }


        /// <summary>
        ///   Restoring unit properties.
        /// </summary>
        [TestMethod]
        public void RestoringUnitProperties()
        {
            // Creating a unit
            var unitName = StringProperty.RandomIdentifier();
            var unit = _domain.AddUnit(unitName);

            // Setting unit properties
            unit.DisplayName = "The unit display name";

            BackupAndRestore();

            var restoredUnit = _domain.GetUnit(unitName);

            Assert.IsNotNull(restoredUnit);

            Assert.AreEqual(restoredUnit.DisplayName, "The unit display name");
        }


        /// <summary>
        ///   Restoring unit users.
        /// </summary>
        [TestMethod]
        public void RestoringUnitUsers()
        {
            // Creating a unit
            var unitName = StringProperty.RandomIdentifier();
            var unit = _domain.AddUnit(unitName);

            // Adding two users to the unit
            var user1Name = StringProperty.RandomIdentifier();
            var user2Name = StringProperty.RandomIdentifier();

            unit.AddUser(_domain.AddUser(user1Name, StringProperty.RandomPassword()));
            unit.AddUser(_domain.AddUser(user2Name, StringProperty.RandomPassword()));

            BackupAndRestore();

            var restoredUnit = _domain.GetUnit(unitName);

            Assert.IsNotNull(restoredUnit);

            Assert.AreEqual(restoredUnit.Users.Count, 2);

            Assert.AreEqual(restoredUnit.Users
                .Count(u => u.Name == user1Name.ToLower()), 1);
            Assert.AreEqual(restoredUnit.Users
                .Count(u => u.Name == user2Name.ToLower()), 1);
        }


        /// <summary>
        ///   Restoring unit hierarchy.
        /// </summary>
        [TestMethod]
        public void RestoringUnitHierarchy()
        {
            // Creating unit hieararchy
            var unitName = StringProperty.RandomIdentifier();
            var child1UnitName = StringProperty.RandomIdentifier();
            var child2UnitName = StringProperty.RandomIdentifier();
            var top1UnitName = StringProperty.RandomIdentifier();
            var top2Unitname = StringProperty.RandomIdentifier();

            var unit = _domain.AddUnit(unitName);
            _domain.AddUnit(top1UnitName).AddUnit(unit);
            _domain.AddUnit(top2Unitname).AddUnit(unit);
            unit.AddUnit(_domain.AddUnit(child1UnitName));
            unit.AddUnit(_domain.AddUnit(child2UnitName));

            BackupAndRestore();

            var restoredUnit = _domain.GetUnit(unitName);

            Assert.IsNotNull(restoredUnit);

            Assert.AreEqual(restoredUnit.ChildUnits.Count, 2);
            Assert.AreEqual(restoredUnit.Parents.Count, 2);

            Assert.AreEqual(restoredUnit.ChildUnits
                .Count(u => u.Name == child1UnitName.ToLower()), 1);
            Assert.AreEqual(restoredUnit.ChildUnits
                .Count(u => u.Name == child2UnitName.ToLower()), 1);
            Assert.AreEqual(restoredUnit.Parents
                .Count(u => u.Name == top1UnitName.ToLower()), 1);
            Assert.AreEqual(restoredUnit.Parents
                .Count(u => u.Name == top2Unitname.ToLower()), 1);
        }


        /// <summary>
        ///   Restoring table permissions.
        /// </summary>
        [TestMethod]
        public void RestoringTablePermissions()
        {
            // Creating two tables
            var table1Name = StringProperty.RandomIdentifier();
            var table2Name = StringProperty.RandomIdentifier();

            var table1 = _domain.AddTable(table1Name);
            var table2 = _domain.AddTable(table2Name);

            // Creating a role and a user
            var roleName = StringProperty.RandomIdentifier();
            var userName = StringProperty.RandomIdentifier();

            var role = _domain.AddRole(roleName);
            var user = _domain.AddUser(userName, StringProperty.RandomPassword());

            // Granting some permissions
            table1.GrantAll(role);
            table2.Grant(user, DataAction.Update, AccessLevel.ChildAndParentUnits);

            BackupAndRestore();

            var restoredTable1 = _domain.GetTable(table1Name);
            var restoredTable2 = _domain.GetTable(table2Name);

            Assert.IsNotNull(restoredTable1);
            Assert.IsNotNull(restoredTable2);

            Assert.AreEqual(restoredTable1.Permissions.Count, 4);
            Assert.AreEqual(restoredTable2.Permissions.Count, 1);

            Assert.AreEqual(restoredTable1.Permissions
                .Count(p => p.Principal.Name == roleName.ToLower()
                && p.DataAction == DataAction.Insert
                && p.AccessLevel == AccessLevel.All), 1);
            Assert.AreEqual(restoredTable1.Permissions
                .Count(p => p.Principal.Name == roleName.ToLower()
                && p.DataAction == DataAction.Select
                && p.AccessLevel == AccessLevel.All), 1);
            Assert.AreEqual(restoredTable1.Permissions
                .Count(p => p.Principal.Name == roleName.ToLower()
                && p.DataAction == DataAction.Update
                && p.AccessLevel == AccessLevel.All), 1);
            Assert.AreEqual(restoredTable1.Permissions
                .Count(p => p.Principal.Name == roleName.ToLower()
                && p.DataAction == DataAction.Delete
                && p.AccessLevel == AccessLevel.All), 1);

            Assert.AreEqual(restoredTable2.Permissions
                .Count(p => p.Principal.Name == userName.ToLower()
                && p.DataAction == DataAction.Update
                && p.AccessLevel == AccessLevel.ChildAndParentUnits), 1);
        }
    }
}
